using System;
using GameFrameworkLibraly.BaseObjects;
using GameFrameworkLibraly.Helpers.GeometryHelpers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;

namespace GameFrameworkLibraly.Controls
{
    public class TrackBarControl:Control
    {
        /// <summary>
        /// Display orientation of this control
        /// </summary>
        public Orientation Orientation = Orientation.Vertical;

        /// <summary>
        /// Texture that will be drawn as a progress
        /// </summary>
        public Texture2D ForeTexture { get; set; }

        /// <summary>
        /// Background texture
        /// </summary>
        public Texture2D BackTexture { get; set; }

        /// <summary>
        /// Defines the thikness of TrackBar small box
        /// </summary>
        public int TrackBarThickness = 30;


        private int _minValue;
        private int _maxValue = 100;
        private int _value;
        private Vector2 _positionStartDragging;
        private Vector2 _positionCurrentDragging;

        private bool _isBeingDragged;

        private Rectangle _rectangleForeTexture;
        private Rectangle _rectangleBackTexture;

        /// <summary>
        /// Occures when progress has been changed
        /// </summary>
        public event EventHandler ProgressChanged;

        /// <summary>
        /// Occures when the foreground is being dragged
        /// </summary>
        public event EventHandler Dragged;

        private void InvokeDragged(EventArgs e)
        {
            EventHandler handler = Dragged;
            if (handler != null) handler(this, e);
        }

        /// <summary>
        /// Occures when dragging end
        /// </summary>
        public event EventHandler<DraggingEndEventArgs> DraggingEnd;

        private void InvokeDraggingEnd(DraggingEndEventArgs e)
        {
            EventHandler<DraggingEndEventArgs> handler = DraggingEnd;
            if (handler != null) handler(this, e);
        }


        /// <summary>
        /// Creates a new instance of TrackBarControl
        /// </summary>
        /// <param name="game">Current game</param>
        /// <param name="destrinationRectangle">Rectangle where texture will be drawn</param>
        /// <param name="backTexture">Texture for back</param>
        /// <param name="foreTexture"></param>
        public TrackBarControl(MyGame game, Rectangle destrinationRectangle, Texture2D backTexture, Texture2D foreTexture) : base(game)
        {
            DestinationRectangle = destrinationRectangle;
            BackTexture = backTexture;
            ForeTexture = foreTexture;
        }


        /// <summary>
        /// Minimal value for this TrackBar (0 as default )
        /// </summary>
        public int MinValue
        {
            get { return _minValue; }
            set
            {
                if (value > MaxValue)
                    throw new ArgumentException("Min value must be less than MaxValue");
                _minValue = value;
            }
        }

        /// <summary>
        /// Maximal value for this TrackBar (100 as default)
        /// </summary>
        public int MaxValue
        {
            get { return _maxValue; }
            set
            {
                if (value < MinValue)
                    throw new ArgumentException("Min value must be more than MinValue");
                _maxValue = value;
            }
        }

        /// <summary>
        /// Current value for this TrackBar (must be between minimum and maximum)
        /// </summary>
        public int Value
        {
            get { return _value; }
            set
            {
                int oldValue = _value;

                if (value >= MaxValue)
                    _value = MaxValue;
                else if (value <= MinValue)
                    _value = MinValue;
                else
                    _value = value;
                if (oldValue != _value)
                    InvokeProgressChanged(EventArgs.Empty);
            }
        }

        protected void InvokeProgressChanged(EventArgs e)
        {
            EventHandler handler = ProgressChanged;
            if (handler != null) handler(this, e);
        }

        /// <summary>
        /// Returns current progress
        /// </summary>
        public float Percentage
        {
            get { return ((float)Value) / (MaxValue - MinValue); }
        }

        #region Control

        /// <summary>
        /// Draws the control
        /// </summary>
        /// <param name="spriteBatch"></param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            if(!Visible)
                return;
            if(DestinationRectangle.IsEmpty)
                return;
            if(BackgroundTexture!=null)
            {
                spriteBatch.Draw(BackgroundTexture,DestinationRectangle,BackColorFilter);
            }
            spriteBatch.Draw(BackTexture, _rectangleBackTexture, ForeColor);
            spriteBatch.Draw(ForeTexture, _rectangleForeTexture, ForeColor);

        }

        

        /// <summary>
        /// Updates state of the control
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            if(!Enabled || DestinationRectangle.IsEmpty)
                return;

            switch (Orientation)
            {
                case Orientation.Horizontal:
                    _rectangleBackTexture = new Rectangle(DestinationRectangle.X,
                                                          DestinationRectangle.Y + DestinationRectangle.Height / 4,
                                                          DestinationRectangle.Width, DestinationRectangle.Height / 2);
                    _rectangleForeTexture = new Rectangle((int)(DestinationRectangle.X + Percentage * DestinationRectangle.Width - TrackBarThickness/2), DestinationRectangle.Y,
                                                          TrackBarThickness, DestinationRectangle.Height);
                    break;
                default:
                    _rectangleBackTexture = new Rectangle(DestinationRectangle.X + DestinationRectangle.Width/4,
                                                          DestinationRectangle.Y, DestinationRectangle.Width/2,
                                                          DestinationRectangle.Height);
                    _rectangleForeTexture = new Rectangle(DestinationRectangle.X,
                                                          (int)(DestinationRectangle.Y + Percentage * DestinationRectangle.Height - TrackBarThickness/2),
                                                          DestinationRectangle.Width, TrackBarThickness);
                    break;
            }
        }

        public override bool ProcessTouch(TouchLocation touch)
        {
            if (!Enabled)
                return false;
            bool touchHandled = false;
            bool isInside = _rectangleForeTexture.Contains((int)touch.Position.X, (int)touch.Position.Y);
            switch (touch.State)
            {

                case TouchLocationState.Pressed:
                    if (isInside)
                    {
                        InvokeClick(EventArgs.Empty);
                        IsPressed = true;
                        TouchID = touch.Id;
                        touchHandled = true;

                        _positionStartDragging = _positionCurrentDragging = touch.Position;
                    }
                    break;
                case TouchLocationState.Moved:
                    if (TouchID.HasValue && TouchID.Value == touch.Id)
                    {
                        IsPressed = isInside;
                        touchHandled = true;

                        _isBeingDragged = true;
                        switch (Orientation)
                        {
                            case Orientation.Horizontal:
                                Value = (int)((touch.Position.X - DestinationRectangle.X) / DestinationRectangle.Width * (MaxValue - MinValue) + MinValue);
                                InvokeDragged(EventArgs.Empty);
                                break;
                            case Orientation.Vertical:
                                Value = (int)((touch.Position.Y - DestinationRectangle.Y) / DestinationRectangle.Height * (MaxValue - MinValue) + MinValue);
                                InvokeDragged(EventArgs.Empty);
                                break;
                        }
                            
                        
                        InvokeDragged(EventArgs.Empty);
                        _positionCurrentDragging = touch.Position;
                    }
                    break;
                case TouchLocationState.Released:
                    if (TouchID.HasValue && TouchID.Value == touch.Id)
                    {
                        TouchID = null;
                        IsPressed = false;
                        touchHandled = true;
                        InvokeReleased(EventArgs.Empty);
                        if (_isBeingDragged)
                        {
                            InvokeDraggingEnd(new DraggingEndEventArgs(_positionStartDragging, _positionCurrentDragging));
                        }
                        _isBeingDragged = false;
                    }
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }


            return touchHandled;
        }

        public override bool ProcessGesture(GestureSample gestureSample)
        {
            if (!Enabled)
                return false;
            switch (gestureSample.GestureType)
            {
                case GestureType.Tap:
                    if (DestinationRectangle.Contains(gestureSample.Position.ToPoint()))
                    {
                        int delta = (MaxValue - MinValue)/20;
                        switch (Orientation)
                        {
                            case Orientation.Horizontal:
                                if (_rectangleForeTexture.Right < gestureSample.Position.X)
                                    Value += delta;
                                else if (_rectangleForeTexture.Left > gestureSample.Position.X)
                                    Value -= delta;
                                break;
                            case Orientation.Vertical:
                                if (_rectangleForeTexture.Top < gestureSample.Position.Y)
                                    Value += delta;
                                else if (_rectangleForeTexture.Bottom > gestureSample.Position.Y)
                                    Value -= delta;
                                break;
                        }
                        InvokeTapped(EventArgs.Empty);
                        return true;
                    }
                    break;
                //case GestureType.HorizontalDrag:
                //    if (Orientation == Orientation.Horizontal && _rectangleForeTexture.Contains(gestureSample.Position.ToPoint()))
                //    {
                //        Value = (int) ((gestureSample.Position.X - DestinationRectangle.X)/DestinationRectangle.Width*(MaxValue-MinValue)+MinValue);
                //        return true;
                //    }
                //    break;
                //case GestureType.VerticalDrag:
                //    if (Orientation == Orientation.Vertical && _rectangleForeTexture.Contains(gestureSample.Position.ToPoint()))
                //    {
                //        Value = (int)((gestureSample.Position.Y - DestinationRectangle.Y) / DestinationRectangle.Height * (MaxValue - MinValue) + MinValue);
                //        return true;
                //    }
                //    break;
            }
            return false;
        }

        #endregion
    }
}
